using Crux.Core.Dapper.SqlGenerator;

namespace Crux.Core.Dapper;

public partial class UnitOfWork<TContext> where TContext : DapperContext
{
    /// <inheritdoc/>
    public TEntity Delete<TEntity>(TEntity entity,
        Action<SqlGenerator<TEntity>>? configure = null) where TEntity : class
    {
        return InternalDelete(entity, configure: configure);
    }
    /// <inheritdoc/>
    public int Delete<TEntity>(Expression<Func<TEntity, bool>> where,
        Action<SqlGenerator<TEntity>>? configure = null) where TEntity : class
    {
        return InternalDelete(where, configure: configure);
    }
    /// <inheritdoc/>
    public int DeleteRange<TEntity>(IEnumerable<TEntity> entities,
        Action<SqlGenerator<TEntity>>? configure = null) where TEntity : class
    {
        return InternalDeleteRange(entities, configure: configure);
    }
    /// <inheritdoc/>
    public async Task<TEntity> DeleteAsync<TEntity>(TEntity entity,
        Action<SqlGenerator<TEntity>>? configure = null, CancellationToken cancellationToken = default)
        where TEntity : class
    {
        return await InternalDeleteAsync(entity, configure: configure, cancellationToken: cancellationToken).ConfigureAwait(false);
    }
    /// <inheritdoc/>

    public async Task<int> DeleteAsync<TEntity>(Expression<Func<TEntity, bool>> where,
        Action<SqlGenerator<TEntity>>? configure = null,
        CancellationToken cancellationToken = default) where TEntity : class
    {
        return await InternalDeleteAsync(where, configure: configure, cancellationToken: cancellationToken).ConfigureAwait(false);
    }
    /// <inheritdoc/>

    public async Task<int> DeleteRangeAsync<TEntity>(IEnumerable<TEntity> entities,
        Action<SqlGenerator<TEntity>>? configure = null,
        CancellationToken cancellationToken = default) where TEntity : class
    {
        return await this.InternalDeleteRangeAsync(entities, configure: configure, cancellationToken: cancellationToken)
            .ConfigureAwait(false);
    }
    /// <inheritdoc/>
    public TEntity PhysicsDelete<TEntity>(TEntity entity,
        Action<SqlGenerator<TEntity>>? configure = null) where TEntity : class
    {
        return InternalDelete(entity, configure: configure);
    }

    /// <inheritdoc/>
    public int PhysicsDelete<TEntity>(Expression<Func<TEntity, bool>> where,
        Action<SqlGenerator<TEntity>>? configure = null) where TEntity : class
    {
        return InternalDelete(where, false, configure);
    }


    /// <inheritdoc/>
    public int PhysicsDeleteRange<TEntity>(IEnumerable<TEntity> entities,
        Action<SqlGenerator<TEntity>>? configure = null) where TEntity : class
    {
        return InternalDeleteRange(entities, false, configure);
    }

    /// <inheritdoc/>
    public async Task<TEntity> PhysicsDeleteAsync<TEntity>(TEntity entity,
        Action<SqlGenerator<TEntity>>? configure = null,
        CancellationToken cancellationToken = default) where TEntity : class
    {
        return await InternalDeleteAsync(entity, false, configure, cancellationToken).ConfigureAwait(false);
    }


    /// <inheritdoc/>
    public async Task<int> PhysicsDeleteAsync<TEntity>(Expression<Func<TEntity, bool>> where,
        Action<SqlGenerator<TEntity>>? configure = null,
        CancellationToken cancellationToken = default) where TEntity : class
    {
        return await InternalDeleteAsync(where, false, configure, cancellationToken).ConfigureAwait(false);
    }


    /// <inheritdoc/>
    public async Task<int> PhysicsDeleteRangeAsync<TEntity>(IEnumerable<TEntity> entities,
        Action<SqlGenerator<TEntity>>? configure = null,
        CancellationToken cancellationToken = default) where TEntity : class
    {
        return await InternalDeleteRangeAsync(entities, false, configure, cancellationToken).ConfigureAwait(false);
    }

    private TEntity InternalDelete<TEntity>(
        TEntity entity,
        bool softDelete = true,
        Action<SqlGenerator<TEntity>>? configure = null) where TEntity : class
    {
        var generator = GetSqlGenerator(configure);

        var sqlQuery = generator.GetDelete(entity, softDelete);

        int effectRow = Execute(sqlQuery.GetSql(), sqlQuery.Param);
        if (logger.IsEnabled(LogLevel.Trace))
            logger.LogTrace($"Delete EffectRow:{effectRow}");

        return entity;
    }

    private int InternalDelete<TEntity>(
        Expression<Func<TEntity, bool>> where,
        bool softDelete = true,
        Action<SqlGenerator<TEntity>>? configure = null) where TEntity : class
    {
        var generator = GetSqlGenerator(configure);

        var sqlQuery = generator.GetDelete(where, softDelete);

        int effectRow = Execute(sqlQuery.GetSql(), sqlQuery.Param);
        if (logger.IsEnabled(LogLevel.Trace))
            logger.LogTrace($"Delete EffectRow:{effectRow}");

        return effectRow;
    }

    private int InternalDeleteRange<TEntity>(
        IEnumerable<TEntity> entities,
        bool softDelete = true,
        Action<SqlGenerator<TEntity>>? configure = null) where TEntity : class
    {
        var generator = GetSqlGenerator(configure);

        int count = 0;
        foreach (var entity in entities)
        {
            var sqlQuery = generator.GetDelete(entity, softDelete);

            int effectRow = Execute(sqlQuery.GetSql(), sqlQuery.Param);

            if (logger.IsEnabled(LogLevel.Trace))
                logger.LogTrace($"Delete EffectRow:{effectRow}");

            count += effectRow;
        }

        return count;
    }

    private async Task<TEntity> InternalDeleteAsync<TEntity>(
        TEntity entity,
        bool softDelete = true,
        Action<SqlGenerator<TEntity>>? configure = null,
        CancellationToken cancellationToken = default) where TEntity : class
    {
        var generator = GetSqlGenerator(configure);

        var sqlQuery = generator.GetDelete(entity, softDelete);

        int effectRow = await ExecuteAsync(sqlQuery.GetSql(), sqlQuery.Param, cancellationToken);
        if (logger.IsEnabled(LogLevel.Trace))
            logger.LogTrace($"Delete EffectRow:{effectRow}");

        return entity;
    }

    private async Task<int> InternalDeleteAsync<TEntity>(
        Expression<Func<TEntity, bool>> where,
        bool softDelete = true,
        Action<SqlGenerator<TEntity>>? configure = null,
        CancellationToken cancellationToken = default) where TEntity : class
    {
        var generator = GetSqlGenerator(configure);

        var sqlQuery = generator.GetDelete(where, softDelete);

        int effectRow = await ExecuteAsync(sqlQuery.GetSql(), sqlQuery.Param, cancellationToken).ConfigureAwait(false);
        if (logger.IsEnabled(LogLevel.Trace))
            logger.LogTrace($"Delete EffectRow:{effectRow}");

        return effectRow;
    }

    private async Task<int> InternalDeleteRangeAsync<TEntity>(
        IEnumerable<TEntity> entities,
        bool softDelete = true,
        Action<SqlGenerator<TEntity>>? configure = null,
        CancellationToken cancellationToken = default) where TEntity : class
    {
        var generator = GetSqlGenerator(configure);

        int count = 0;
        foreach (var entity in entities)
        {
            var sqlQuery = generator.GetDelete(entity, softDelete);

            int effectRow = await ExecuteAsync(sqlQuery.GetSql(), sqlQuery.Param, cancellationToken)
                .ConfigureAwait(false);

            if (logger.IsEnabled(LogLevel.Trace))
                logger.LogTrace($"Delete EffectRow:{effectRow}");

            count += effectRow;
        }

        return count;
    }
}